package com.game;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.nio.channels.SocketChannel;
import java.util.LinkedList;
import java.util.List;

import org.apache.log4j.Logger;

import com.common.utilities.ReceiveData;
import com.common.utilities.SendData;
import com.server.NioServer;
import com.server.ServerDataEvent;

/**
 * User: wattsc5
 * <p/>
 * Date: 2/26/12
 */
public class ServerGameWorker implements Runnable
{
	private static final Logger logger = Logger.getLogger(ServerGameWorker.class);

	private final List<ServerDataEvent> queue = new LinkedList<ServerDataEvent>();

	private ServerGameAPI api;

	private NioServer server;

	public void setApi(ServerGameAPI api)
	{
		this.api = api;
	}

	public void setServer(NioServer server)
	{
		this.server = server;
	}

	/**
	 * Processes the data received from the client.
	 *
	 * @param socket the socket that the data belongs to.
	 * @param data the data that was received.
	 */
	public void processData(SocketChannel socket, byte[] data)
	{
		List<ReceiveData> receiveDataList;

		try
		{
			receiveDataList = ReceiveData.extractDataSets(data, socket);
		}
		catch (IOException ex)
		{
			SendData sendData = new SendData();
			sendData.addArgument("error");
			sendData.addArgument("Bad input - " + ex.getMessage());
			logger.warn("Bad input - " + socket.socket().getInetAddress() + " - " + ex);
			this.addDataToSend(socket, sendData);
			return;
		}

		try
		{
			for (ReceiveData dataSet: receiveDataList)
			{
				logger.trace("Calling command from - " + socket.socket().getInetAddress() + ":" + socket.socket().getPort() + " - " + dataSet.getCommandName());
				Class.forName("com.game.ServerGameAPI").getMethod(dataSet.getCommandName(), ReceiveData.class).invoke(this.api, dataSet);
			}
		}
		catch (IllegalAccessException e)
		{
			logger.error(e);
			logger.error("Received data from - " + socket.socket().getInetAddress() + ":" + socket.socket().getPort() + " - " + new String(data));
			e.printStackTrace();
		}
		catch (InvocationTargetException e)
		{
			logger.error(e);
			logger.error("Received data from - " + socket.socket().getInetAddress() + ":" + socket.socket().getPort() + " - " + new String(data));
			e.printStackTrace();
		}
		catch (NoSuchMethodException e)
		{
			logger.error(e);
			logger.error("Received data from - " + socket.socket().getInetAddress() + ":" + socket.socket().getPort() + " - " + new String(data));
			e.printStackTrace();
		}
		catch (ClassNotFoundException e)
		{
			logger.error(e);
			logger.error("Received data from - " + socket.socket().getInetAddress() + ":" + socket.socket().getPort() + " - " + new String(data));
			e.printStackTrace();
		}
	}

	public void addDataToSend(SocketChannel socket, SendData sendData)
	{
		synchronized (this.queue)
		{
			if (server == null)
			{
				logger.error("Server null in game worker. Cannot send message");
				return;
			}

			this.queue.add(new ServerDataEvent(server, socket, sendData.getDataAsString().getBytes()));
			this.queue.notify();
		}
	}

	public void disconnectClient(SocketChannel socketChannel)
	{
		this.server.disconnectClientFromServer(socketChannel);
	}

	public void run()
	{
		ServerDataEvent event;

		while (true)
		{
			// Wait for the data to become available
			synchronized (this.queue)
			{
				while (this.queue.isEmpty())
				{
					try
					{
						this.queue.wait();
					}
					catch (InterruptedException e)
					{
						logger.error(e);
					}
				}

				event = this.queue.remove(0);
			}

			this.server.send(event.getSocket(), event.getData());
		}
	}

	public void notifyClientDisconnected(SocketChannel socketChannel)
	{
		this.api.playerDisconnected(socketChannel);
	}
}
